#
# A curses helper library with nifty effects
#
# Sergio Rubio <rubiojr@biondofu.net>
# 

import curses
import sys, os
import time
import random
from random import randrange

SCRAMBLE_CHARS = ['a', 'b','c','d','e','f','g','h','i','j','k','l',
					'm', 'n','o','p','q','r','s','t','u','v','w','x','y','z']

class TextMenu:

	# options is a dict in the form key:
	def __init__(self, window, title, options):
		self.TITLE_COLOR = 1
		self.OPTION_COLOR = 2
		self.OPTION_DESC_COLOR = 3
		self.FX_CRYPTO1 = self.__draw_with_crypto_effect1
		self.FX_CRYPTO2 = self.__draw_with_crypto_effect2
		self.FX_CRYPTO3 = self.__draw_with_crypto_effect3
		self.FX_OPTION_DELAY = self.__draw_with_option_delay_effect
		
		self.draw_effect = self.FX_OPTION_DELAY
		self.title = title
		self.options = options
		self.x = 0
		self.y = 0
		self.window = window
		# title colors
		self.title_fgcolor = curses.COLOR_WHITE
		self.title_bgcolor = curses.COLOR_BLACK
		curses.init_pair(1, self.title_fgcolor, self.title_bgcolor)
		
		# option key color
		self.option_fgcolor = curses.COLOR_GREEN
		self.option_bgcolor = curses.COLOR_BLACK
		curses.init_pair(2, self.option_fgcolor, self.option_bgcolor)

		# option description color
		self.option_desc_fgcolor = curses.COLOR_WHITE
		self.option_desc_bgcolor = curses.COLOR_BLACK
		curses.init_pair(3, self.option_desc_fgcolor, self.option_desc_bgcolor)

	def set_position(self, x, y):
		pass

	def show(self):
		self.__draw()

	def __draw(self):
		opt_offset = 2
		self.window.addstr(self.y, self.x, self.title, curses.color_pair(self.TITLE_COLOR))
		self.y = self.y + opt_offset
		self.__draw_with_effect(self.x, self.y, self.options)

	def __draw_with_effect(self, x, y, options):
		#self.__draw_with_option_delay_effect(x, y, key, value)
		self.draw_effect(x, y, options)
	
	def __draw_with_option_delay_effect(self, x, y, options):
		self.window.immedok(1)
		for key, value in self.options:
			self.window.addstr(self.y, self.x, str(key) + ")", curses.color_pair(self.OPTION_COLOR))
			self.window.addstr(self.y, self.x + 3, str(value), curses.color_pair(self.OPTION_DESC_COLOR))
			curses.delay_output(600)
			self.y = self.y + 1

	def __draw_with_crypto_effect1(self, x, y, options):
		self.window.immedok(1)
		for key, value in self.options:
			self.window.addstr(self.y, self.x, str(key) + ")", curses.color_pair(self.OPTION_COLOR))
			rstart = 0
			milisecs = 0
			msg_pointer = 1
			run = 1

			while run:
				for j in range(0, 3):
					for i in range (rstart, msg_pointer):
						 self.window.addch(self.y, i + self.x + 3, ord(SCRAMBLE_CHARS[ randrange(0, len(SCRAMBLE_CHARS) - 1 )]))
					curses.delay_output(1)

				curses.delay_output(5)
				milisecs += 10
				if msg_pointer < len(value):
						msg_pointer += 1
						
				if milisecs >= 10 and msg_pointer == len(value):
					self.window.addstr(self.y, rstart + self.x + 3 , value[rstart])
					rstart += 1			
					milisecs = 0

				if rstart == len(value):
					run = 0
			self.y = self.y + 1
	
	# step + crypto effect
	def __draw_with_crypto_effect2(self, x, y, options):
		self.window.immedok(1)
		for key, value in self.options:
			self.window.addstr(self.y, self.x, str(key) + ")", curses.color_pair(self.OPTION_COLOR))
			rstart = 0
			offset = 0
			end_range = 0
			init_delay = 1
			val_list = []
			cracked = []

			for i in range(0, len(value)):
				val_list.append (i)
				
			random.shuffle(val_list)

			while len(val_list) > 0:
				for j in range(0, 10):
					for i in val_list:
						if i not in cracked:
							self.window.addch(self.y, i + self.x + 3, ord(SCRAMBLE_CHARS[ randrange(0, len(SCRAMBLE_CHARS) - 1 )]))
					curses.delay_output(1)

				c = val_list.pop()
				self.window.addstr(self.y, c + self.x + 3 , value[c])
				cracked.append (c)
				rstart += 1			
				offset += 1

				if rstart == len(value):
					run = 0
				
			self.y = self.y + 1
		

	def __randomize_number_list(self, n):
		l = []
		for i in range(0, n):
			l.append(i)
		random.shuffle(l)
		return l

	def __draw_with_crypto_effect3(self, x, y, options):
		#lets optimize a little bit with manual refresh
		self.window.immedok(0)

		#holds one list per option with the order of characters to crack/fix
		cracking_list = []
		cracked_lists = []

		cracked_descriptions = []
		first_round = 1
		crack_count = 0
		total_tocrack = 0
		cracked = 0
		for option, description in options:
			dlen = len(description)
			total_tocrack += dlen
			cracking_list.append( self.__randomize_number_list(dlen) )
			cracked_descriptions.append([])

		while crack_count < total_tocrack:
			# play with the range to change the decryption effect speed	
			for j in range(0, 5):
				for n in range(len(options)):
					option, description = options[n]
					for i in range(0, len(description)):
						desc_offset = i + 3
						if first_round:
							self.window.addstr(y, x, str(option) + ")", curses.color_pair(self.OPTION_COLOR))
						if i not in cracked_descriptions[n]:
							self.window.addch(y, x + desc_offset, ord(SCRAMBLE_CHARS[ randrange(0, len(SCRAMBLE_CHARS) - 1 )]))
					y += 1

				self.window.refresh()
				first_round = 0
				y -= len(options)
				curses.delay_output(1)

			for i in range(0, len (cracking_list)):
				if len(cracking_list[i]) > 0:
					char_found = cracking_list[i].pop()
					k, v = options[i]
					self.window.addstr(y + i, char_found + x + 3 , v[char_found])
					self.window.refresh()
					cracked_descriptions[i].append(char_found)
					crack_count += 1
					self.window.refresh()
		

				
def menu_demo(win):
	menu = TextMenu(win, "Test menu", [ ("1", "Activate this option with 1"),
												("2", "Activate this option with 2"),
												("3", "Activate this option with 3"),
												("4","A shorter option") ])
	menu.x = 4
	menu.y = 2
	menu.draw_effect = menu.FX_CRYPTO3
	menu.show()
	win.addstr (9, 3, "Cracking complete!")
	win.refresh()

					
def effects_demo(win):
	run = 1
	spacing = 8
	while run:

		for i in range(0,4):
			win.refresh()
			menu = TextMenu(win, "Menu Title", [ ("1", "Menu option 1"),
														("2", "Menu option 2"),
														("3", "Menu option 3") ])
			menu.x = 4
			menu.y = 2 + (spacing * i)
			if i == 0:
				menu.title = "FX CRYPTO Effect 3:"
				menu.draw_effect = menu.FX_CRYPTO3
			elif i == 1:
				menu.title = "FX CRYPTO Effect 2:"
				menu.draw_effect = menu.FX_CRYPTO2
			elif i == 2:
				menu.title = "FX CRYPTO Effect 1:"
				menu.draw_effect = menu.FX_CRYPTO1
			else:
				menu.title = "FX OPTION DELAY Effect:"
				menu.draw_effect = menu.FX_OPTION_DELAY
			cursor = True
			while 1:
				if cursor:
					win.addstr(menu.y -1, menu.x, "Press n to next demo  ")
					cursor = False
				else:
					win.addstr(menu.y -1, menu.x, "Press n to next demo _")
					cursor = True
				win.refresh()
				curses.delay_output(100)
				ch = win.getch()
				if ch == ord('n'):
					break
				elif ch != -1:
					win.refresh()
			menu.show()
		win.clear()
	

def main(win):
	curses.curs_set(0)
	win.nodelay(1)
	win.immedok(1)
	win.clear()
	win.border()
	effects_demo(win)

curses.wrapper(main)	
